/**
* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/

/*!
 * \file platform_ascendc.cpp
 * \brief
 */

#include <mutex>
#include <dlfcn.h>
#include <csignal>
#include "securec.h"
#include "platform/platform_info.h"
#include "platform_ascendc_log.h"
#include "platform/platform_infos_def.h"
#include "platform_ascendc.h"

namespace platform_ascendc {
const static uint64_t LOCAL_RESERV_SIZE = 256;
const static uint32_t WORK_SPACE_SIZE_910B = 16 * 1024 * 1024;
const static uint32_t WORK_SPACE_SIZE_910_95 = 16 * 1024 * 1024;
const static uint32_t WORK_SPACE_SIZE_910_55 = 16 * 1024 * 1024;
const static uint32_t WORK_SPACE_SIZE = 2 * 1024 * 1024;
const static uint32_t MIX_AIC_AIV_RATION_910B1 = 2;
const static uint32_t CUBE_GROUP_WORK_SPACE_SIZE_910B = 1 * 1024 * 1024;
const static uint32_t GROUP_BARRIER_WORK_SPACE_SIZE_910B = 1 * 1024 * 1024;
const static std::string LABEL_VERSION = "version";
const static std::string LABEL_SOC_INFO = "SoCInfo";
const static std::string LABEL_SHORT_SOC_VERSION = "Short_SoC_version";
const static std::string LABEL_SPLIT_KEY = "core_type_list";
const static std::string LABEL_SPLIT_VAL = "CubeCore,VectorCore";
const static std::string LABEL_CORE_CNT_CUB = "cube_core_cnt";
const static std::string LABEL_CORE_CNT_VEC = "vector_core_cnt";
const static std::string LABEL_CORE_CNT_AICORE = "ai_core_cnt";

static inline uint32_t GetCoreNumByType(fe::PlatFormInfos *platformInfo, bool isAiv)
{
    std::string key;
    std::string val;
    bool ret = platformInfo->GetPlatformResWithLock(LABEL_SOC_INFO, LABEL_SPLIT_KEY, val);
    if (!ret) {
        PF_LOGE("get platform failed, val is %s", val.c_str());
    }
    if (LABEL_SPLIT_VAL.compare(val) != 0) {
        key = LABEL_CORE_CNT_AICORE;
    } else if (isAiv) {
        key = LABEL_CORE_CNT_VEC;
    } else {
        key = LABEL_CORE_CNT_CUB;
    }
    ret = platformInfo->GetPlatformResWithLock(LABEL_SOC_INFO, key, val);
    if (!ret) {
        PF_LOGE("get platform failed, key is %s, val is %s", key.c_str(), val.c_str());
    }
    return val.empty() ? 0 : static_cast<uint32_t>(std::atoi(val.c_str()));
}

uint32_t PlatformAscendC::GetCoreNumVector(void) const
{
    if (GetSocVersion() == SocVersion::ASCEND310P) {
        std::string val;
        bool ret = this->GetPlatFormInfo()->GetPlatformResWithLock(LABEL_SOC_INFO, LABEL_CORE_CNT_VEC, val);
        if (!ret) {
            PF_LOGE("get platform vector num failed, val is %s", val.c_str());
        }
        uint32_t vecCoreNum = val.empty() ? 0 : std::atoi(val.c_str());
        return vecCoreNum;
    }
    return 0;
}

uint32_t PlatformAscendC::GetCoreNumAic(void) const
{
    return GetCoreNumByType(this->GetPlatFormInfo(), false);
}

uint32_t PlatformAscendC::GetCoreNumAiv(void) const
{
    return GetCoreNumByType(this->GetPlatFormInfo(), true);
}

uint32_t PlatformAscendC::GetCoreNum(void) const
{
    return this->GetPlatFormInfo()->GetCoreNum();
}

void PlatformAscendC::GetCoreMemSize(const CoreMemType &memType, uint64_t &size) const
{
    const fe::LocalMemType localType = static_cast<fe::LocalMemType>(memType);
    this->GetPlatFormInfo()->GetLocalMemSize(localType, size);
    // only ascend910B need UB/L1 local reserved buf for kfc
    if ((memType == CoreMemType::UB || memType == CoreMemType::L1)
         && GetSocVersion() == SocVersion::ASCEND910B) {
        size -= LOCAL_RESERV_SIZE;
    }
    if (memType == CoreMemType::UB) {
        size -= reservedMemSize_;
    }

    if (memType == CoreMemType::FB) {
        std::string sizeStr;
        bool ret = this->GetPlatFormInfo()->GetPlatformResWithLock("AICoreSpec", "fb0_size", sizeStr);
        if (!ret) {
            PF_LOGE("platform do not support get CoreMemType::FB size !");
            return;
        }
        size = sizeStr.empty() ? 0 : std::stoull(sizeStr);
    }
 
    if (memType == CoreMemType::BT) {
        std::string sizeStr;
        bool ret = this->GetPlatFormInfo()->GetPlatformResWithLock("AICoreSpec", "bt_size", sizeStr);
        if (!ret) {
            PF_LOGE("platform do not support get CoreMemType::BT size !");
            return;
        }
        size = sizeStr.empty() ? 0 : std::stoull(sizeStr);
    }
}

void PlatformAscendC::ReserveLocalMemory(ReservedSize size)
{
    static std::map<ReservedSize, uint32_t> reservedSizeMap = {
        {ReservedSize::RESERVED_SIZE_8K, 8 * 1024},
        {ReservedSize::RESERVED_SIZE_16K, 16 * 1024},
        {ReservedSize::RESERVED_SIZE_32K, 32 * 1024},
    };
    auto it = reservedSizeMap.find(size);
    if (it == reservedSizeMap.end()) {
        PF_LOGE("failed to find ReserveSize, current enum id is %d", static_cast<int32_t>(size));
    } else {
        reservedMemSize_ = it->second;
    }
}

SocVersion PlatformAscendC::GetSocVersion(void) const
{
    std::string socVersionStr;
    const auto ret = this->GetPlatFormInfo()->GetPlatformResWithLock(LABEL_VERSION, LABEL_SHORT_SOC_VERSION, socVersionStr);
    if (!ret) {
        PF_LOGE("get platform failed, socVersionStr is %s", socVersionStr.c_str());
        return SocVersion::RESERVED_VERSION;
    }
    static std::map<std::string, SocVersion> convertMap = {
        {"Ascend310P", SocVersion::ASCEND310P},
        {"Ascend910", SocVersion::ASCEND910},
        {"Ascend910B", SocVersion::ASCEND910B},
        {"Ascend910_93", SocVersion::ASCEND910B},
        {"Ascend310B", SocVersion::ASCEND310B},
        {"Ascend910_95", SocVersion::ASCEND910_95},
        {"Ascend910_55", SocVersion::ASCEND910_55},
        {"AS31XM1", SocVersion::AS31XM1},
        {"Ascend031", SocVersion::ASCEND031},
        {"Ascend035", SocVersion::ASCEND035},
        {"Ascend310", SocVersion::ASCEND310},
        {"Ascend610", SocVersion::ASCEND610},
        {"Ascend610Lite", SocVersion::ASCEND610Lite},
        {"BS9SX1A", SocVersion::BS9SX1A},
        {"BS9SX2A", SocVersion::BS9SX2A},
        {"Hi3796CV300CS", SocVersion::HI3796CV300CS},
        {"Hi3796CV300ES", SocVersion::HI3796CV300ES},
        {"MC61AM21A", SocVersion::MC61AM21A},
        {"MC62CM12A", SocVersion::MC62CM12A},
        {"SD3403", SocVersion::SD3403},
        {"KirinX90", SocVersion::KIRINX90}
    };
    auto it = convertMap.find(socVersionStr);
    if (it != convertMap.end()) {
        return it->second;
    }
    PF_LOGE("get platform failed, convertMap do not find soc %s version", socVersionStr.c_str());
    return SocVersion::RESERVED_VERSION;
}
void PlatformAscendC::GetCoreMemBw(const CoreMemType &memType, uint64_t &bwSize) const
{
    const fe::LocalMemType localType = static_cast<fe::LocalMemType>(memType);
    this->GetPlatFormInfo()->GetLocalMemBw(localType, bwSize);
}

fe::PlatFormInfos* PlatformAscendC::GetPlatFormInfo(void) const
{
    ASCENDC_ASSERT(this->platformInfo_ != nullptr, PF_LOGE("PlatformInfo cannot be initialized to nulltpr!!"));
    return this->platformInfo_;
}

uint32_t PlatformAscendC::CalcTschBlockDim(uint32_t sliceNum, uint32_t aicCoreNum, uint32_t aivCoreNum) const
{
    if (aicCoreNum == 0 || aivCoreNum == 0 || aicCoreNum > aivCoreNum) {
        return sliceNum;
    }
    uint32_t ration = aivCoreNum / aicCoreNum;
    uint32_t blockDim = (sliceNum + (ration - 1)) / ration;
    // in mix case: 910B1(ration = 2), blockDim should not be greater than physical aic core num
    if ((ration == MIX_AIC_AIV_RATION_910B1) && (blockDim > aicCoreNum)) {
        PF_LOGE("CalcTschBlockDim failed, calc blockDim %u should not be greater than aicCoreNum %u", blockDim,
            aicCoreNum);
        return 0;
    }
    return blockDim;
}

uint32_t PlatformAscendC::GetLibApiWorkSpaceSize(void) const
{
    auto socVersion = GetSocVersion();
    if (socVersion == SocVersion::RESERVED_VERSION) {
        PF_LOGE("get platform failed, socVersionStr is %d", socVersion);
        return -1;
    } else if (socVersion == SocVersion::ASCEND910B) {
        return WORK_SPACE_SIZE_910B;
    } else if (socVersion == SocVersion::ASCEND910_95) {
        return WORK_SPACE_SIZE_910_95;
    } else if (socVersion == SocVersion::ASCEND910_55) {
        return WORK_SPACE_SIZE_910_55;
    }
    return WORK_SPACE_SIZE;
}

uint32_t PlatformAscendC::GetResCubeGroupWorkSpaceSize(void) const
{
    auto socVersion = GetSocVersion();
    if (socVersion == SocVersion::ASCEND910B) {
        return CUBE_GROUP_WORK_SPACE_SIZE_910B;
    } else {
        PF_LOGE("get platform failed, socVersionStr is %d", socVersion);
        return -1;
    }
}

uint32_t PlatformAscendC::GetResGroupBarrierWorkSpaceSize(void) const
{
    auto socVersion = GetSocVersion();
    if (socVersion == SocVersion::ASCEND910B) {
        return GROUP_BARRIER_WORK_SPACE_SIZE_910B;
    } else {
        PF_LOGE("get platform failed, socVersionStr is %d", socVersion);
        return -1;
    }
}

PlatformAscendC* PlatformAscendCManager::platformInfo = nullptr;
std::mutex PlatformAscendCManager::platformInitMtx;
SocVersion PlatformAscendCManager::SocVersionMap(const char *socVersionStr)
{
    static std::map<std::string, SocVersion> convertMap = {
        {"Ascend310P", SocVersion::ASCEND310P},
        {"Ascend910", SocVersion::ASCEND910},
        {"Ascend910B", SocVersion::ASCEND910B},
        {"Ascend910_93", SocVersion::ASCEND910B},
        {"Ascend310B", SocVersion::ASCEND310B},
        {"Ascend910_95", SocVersion::ASCEND910_95},
        {"Ascend910_55", SocVersion::ASCEND910_55},
        {"MC62CM12A", SocVersion::MC62CM12A}
    };
    const auto &iter = convertMap.find(socVersionStr);
    if (iter != convertMap.end()) {
        return iter->second;
    }
    PF_LOGE("get platform failed, convertMap do not find soc %s version", socVersionStr);
    return SocVersion::RESERVED_VERSION;
}

namespace {
#ifdef ASCEND_IS_AICPU
bool SwitchIntoShortSocVersion(const char *socVersionStr, std::string &shortSocVersion)
{
    const static std::map<std::string, std::string> convertMap = {
        {"Ascend910B1", "Ascend910B"}, // ascend910b_list
        {"Ascend910B2", "Ascend910B"},
        {"Ascend910B2C", "Ascend910B"},
        {"Ascend910B3", "Ascend910B"},
        {"Ascend910B4", "Ascend910B"},
        {"Ascend910B4-1", "Ascend910B"},
        {"Ascend910_9391", "Ascend910B"},
        {"Ascend910_9381", "Ascend910B"},
        {"Ascend910_9372", "Ascend910B"},
        {"Ascend910_9392", "Ascend910B"},
        {"Ascend910_9382", "Ascend910B"},
        {"Ascend910_9362", "Ascend910B"},
        {"Ascend910A","Ascend910"}, // ascend910_list
        {"Ascend910ProA","Ascend910"},
        {"Ascend910B","Ascend910"},
        {"Ascend910ProB","Ascend910"},
        {"Ascend910PremiumA","Ascend910"},
        {"Ascend310P1", "Ascend310P"}, // ascend310p_list
        {"Ascend310P3", "Ascend310P"},
        {"Ascend310B1", "Ascend310B"}, // ascend310b_list
        {"Ascend310B2", "Ascend310B"},
        {"Ascend310B3", "Ascend310B"},
        {"Ascend310B4", "Ascend310B"},
        {"Ascend910_957b", "Ascend910_95"},
        {"Ascend910_950z", "Ascend910_95"},
        {"Ascend910_958b", "Ascend910_95"},
        {"Ascend910_958a", "Ascend910_95"},
        {"Ascend910_9599", "Ascend910_95"},
        {"Ascend910_957d", "Ascend910_95"},
        {"Ascend910_9581", "Ascend910_95"},
        {"Ascend910_957c", "Ascend910_95"},
        {"Ascend910_9589", "Ascend910_95"}, // ascend910_95_list
        {"MC62CM12AA", "MC62CM12A"}
    };
    const auto &iter = convertMap.find(socVersionStr);
    if (iter != convertMap.end()) {
        shortSocVersion = iter->second;
        return true;
    }
    PF_LOGE("get platform failed, convertMap does not find short soc %s version", socVersionStr);
    return false;
}

bool GetShortSocVersion(std::string &shortSocVersion)
{
    void* handle = dlopen("libascend_hal.so", RTLD_LAZY);
    if (handle == nullptr) {
        PF_LOGE("Failed to get short soc version in aicpu, cannot dlopen ascend_hal so");
        return false;
    }
    int (*rtGetSocVersion)(uint32_t, char *, uint32_t);
    rtGetSocVersion =
        reinterpret_cast<int(*)(uint32_t, char *, uint32_t)>(dlsym(handle, "halGetSocVersion"));
    if (rtGetSocVersion == nullptr) {
        dlclose(handle);
        PF_LOGE("Failed to get short soc version in aicpu, cannot dlsym symbol halGetSocVersion");
        return false;
    }
    constexpr unsigned int len = 32;
    constexpr uint32_t deviceId = 0;
    char socVersionStr[len] = {0};
    int32_t ret = rtGetSocVersion(deviceId, socVersionStr, len);
    dlclose(handle);
    if (ret != 0) {
        PF_LOGW("Failed to get short soc version in aicpu len = [%d], socVersionStr = [%s], "
                "set soc version to Ascend310P by default", static_cast<int32_t>(len), socVersionStr);
        shortSocVersion = "Ascend310P";
    } else {
        bool retShort = SwitchIntoShortSocVersion(socVersionStr, shortSocVersion);
        if (!retShort) {
            PF_LOGE("Failed to get short soc version in aicpu, "
                    "cannot switch into short soc version, socVersionStr = [%s]", socVersionStr);
            return false;
        }
    }
    return true;
}
#else
bool GetRealSocVersion(std::string &realSocVersion)
{    
    void *handle = dlopen("libruntime.so", RTLD_LAZY);
    if (handle == nullptr) {
        PF_LOGE("Failed to get short soc version, cannot dlopen runtime so");
        return false;
    }

    void (*rtGetSocVersion)(char *, const uint32_t);
    rtGetSocVersion =
        reinterpret_cast<void(*)(char *, const uint32_t)>(dlsym(handle, "rtGetSocVersion"));
    if (rtGetSocVersion == nullptr) {
        dlclose(handle);
        PF_LOGE("Failed to get short soc version, cannot dlsym symbol rtGetSocVersion");
        return false;
    }
    char socVersion[50];
    rtGetSocVersion(&(socVersion[0]), sizeof(socVersion));
    dlclose(handle);
    realSocVersion = socVersion;
    return true;
}
#endif
} // namespace

#ifdef ASCEND_IS_AICPU
fe::PlatFormInfos* PlatformAscendCManager::PlatformAscendCInit(const char *customSocVersion)
{
    std::string shortSocVersion;
    if (customSocVersion == nullptr) {
        bool ret = GetShortSocVersion(shortSocVersion);
        if (!ret) {
            PF_LOGE("Failed to run platform ascendc init in aicpu, GetShortSocVersion failed");
            return nullptr;
        }
    } else {
        bool retShort = SwitchIntoShortSocVersion(customSocVersion, shortSocVersion);
        if (!retShort) {
            PF_LOGE("Failed to PlatformAscendCInit in aicpu, "
                    "cannot switch into short soc version, customSocVersion = [%s]", customSocVersion);
            return nullptr;
        }
    }

    std::map<std::string, std::string> shortSocVersionMap;
    shortSocVersionMap[LABEL_SHORT_SOC_VERSION] = shortSocVersion;
    static fe::PlatFormInfos gPlatformInfo;
    gPlatformInfo.Init();
    gPlatformInfo.SetPlatformResWithLock(LABEL_VERSION, shortSocVersionMap);
    return &gPlatformInfo;
}
#else
fe::PlatFormInfos* PlatformAscendCManager::PlatformAscendCInit(const char *customSocVersion)
{
    std::string socVersion;
    if (customSocVersion == nullptr) {
        bool ret = GetRealSocVersion(socVersion);
        if (!ret) {
            PF_LOGE("Failed to run platform ascendc init, GetRealSocVersion failed");
            return nullptr;
        }
    } else {
        socVersion = customSocVersion;
    }

    fe::PlatformInfoManager::GeInstance().InitRuntimePlatformInfos(socVersion);
    fe::OptionalInfos optionalInfos;
    static fe::PlatFormInfos gPlatformInfo;
    fe::PlatformInfoManager::GeInstance().GetPlatformInfos(socVersion,
        gPlatformInfo, optionalInfos);
    std::string socVersionStr;
    const auto ret = gPlatformInfo.GetPlatformResWithLock(LABEL_VERSION, LABEL_SHORT_SOC_VERSION, socVersionStr);
    if (!ret) {
        PF_LOGE("get platform short version failed by fe, socVersion is %s", socVersion.c_str());
    }
    SocVersion version = SocVersionMap(socVersionStr.c_str());
    if (version == SocVersion::RESERVED_VERSION) {
        PF_LOGE("Invalid SocVersion.");
        return nullptr;
    } else if ((version == SocVersion::ASCEND310P) || (version == SocVersion::ASCEND910)) {
        gPlatformInfo.SetCoreNumByCoreType("AiCore");
    } else {
        gPlatformInfo.SetCoreNumByCoreType("VectorCore");
    }
    return &gPlatformInfo;
}
#endif

PlatformAscendC* PlatformAscendCManager::PlatformAscendCManagerInit(const char *customSocVersion)
{
    static fe::PlatFormInfos* gPlatformAscendCInfo = PlatformAscendCInit(customSocVersion);
    if (gPlatformAscendCInfo == nullptr) {
        return nullptr;
    }
    static PlatformAscendC tmp(gPlatformAscendCInfo);
    platformInfo = &tmp;
    return platformInfo;
}
}
